#include <openssl/aes.h>
#include <string>
#include <memory>

#define PRINT_AFTER_AES_ENCRYPT (1)

bool AES_cbc_encrypt_json(std::string & req, std::string & res)
{
    AES_KEY aes;
	bool ret = true;
    /* 初始向量（一般为16字节全0）*/
    unsigned char ivec[AES_BLOCK_SIZE] = {0};

    /* 设置用户密钥 */
    std::string key = "AAAABBBBCCCCDDDD";

    /* 检查密钥合法性 */
    ret = ((key.size() == 16)||(key.size() == 24)||(key.size() == 32));
    if(!ret)
    {
        printf("Error AES Key:%s", key.c_str());
        return ret;
    }

    /* 设置生成加密密钥 */
    AES_set_encrypt_key((const unsigned char*)key.c_str(), key.size() * 8, &aes);

    /*  分组密码算法中需要将明文按指定大小进行分组，由于明文并非指定大小的整数倍，
     *因此在明文的最后一个分组需要将其填充至加密算法所要求的分组大小后进行加密。
     *  所以如果需要处理任意长度的数据，那么需要在原始数据末尾先进行填充，
     *使得数据长度为16的整数倍，随后再分块进行加密。
     *  json数据中添加空格进行填充，将不会对数据造成影响。
     */
    int remainder = req.length() % 16;
    if(0 != remainder)
    {
        int appendLen = 16 - remainder;
        req = req + std::string(appendLen, ' ');
    }
    std::unique_ptr< char[]> pOut(new char[req.length() + 1]);
    pOut[req.length()] = 0;
    AES_cbc_encrypt((const unsigned char*)req.c_str(), ( unsigned char*)pOut.get(), req.length(), &aes, ivec, AES_ENCRYPT);

    #if PRINT_AFTER_AES_ENCRYPT
        /**
         * AES加密/解密在线验证网址
         * https://the-x.cn/cryptography/Aes.aspx
         */
        printf("After AES_cbc_encrypt:\n");
        for(int i = 0; i < req.length(); i++)
        {
            printf("%02x ", pOut[i] & 0xff);
        }
        printf("\n");
    #endif

    /* 使用string存储加密后的报文可能不是一种合理的方式，因为加密后的报文是 unsigned char类型 */
    res = std::string(pOut.get(), req.length());
	return ret;
}

int main(int argc, const char *argv[])
{
	
    std::string req = R"("{"name":"Tom"}")";
	std::string res;

	AES_cbc_encrypt_json(req, res);

	return 0;
}
